package knapsack.core;

import knapsack.data.InstanceContext;
import knapsack.data.KnapsackInstanceContext;
import knapsack.strategies.EvolutionaryKnapsack;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.List;

/**
 * This class encapsulates the measurement stuffs of a knapsack problem.
 *
 * @author vasek
 */
public class KnapsackEvaluator {

    /**
     * Main method.
     *
     * @param args command line arguments
     * @throws IOException when resource with instances not found
     */
    public static void main(final String[] args) throws IOException {
        InstanceContext context = KnapsackInstanceContext.getContext(true);
        List<KnapsackInstance> instances = context.getInstances(40);

        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        double[] relativeErrors = new double[instances.size()];
        final long beginCPUTime = threadBean.getCurrentThreadCpuTime();
        int i = 0;

        for (KnapsackInstance instance : instances) {
            if (i == 0) {
                final KnapsackStrategy strategy = new EvolutionaryKnapsack(instance);
                strategy.run();
                final int cost = strategy.getKnapsackCost();
                relativeErrors[i] = relativeError(instance.getSolutionCost(), cost);
                i++;
                System.out.println("Computed cost is: " + cost);
            }
        }

        final long endCPUTime = threadBean.getCurrentThreadCpuTime();
        final long cpuTime = (long) (((endCPUTime - beginCPUTime) / instances.size()) * Math.pow(10, -6));
        System.out.println("Average CPU time: " + cpuTime + " ms");
        System.out.println("Average relative error: " + averageRelativeError(relativeErrors));
    }

    /**
     * Computes average relative error from the array of particular relative errors.
     *
     * @param errors particular relative errors
     * @return average relative error
     */
    private static double averageRelativeError(final double[] errors) {

        double sum = 0.0;
        for (double error : errors) {
            sum += error;
        }

        return sum / errors.length;
    }

    /**
     * Compute relative error for a particular instance.
     *
     * @param optimalCost     cost of the exact solution
     * @param approximateCost cost of the approximate solution
     * @return relative error between exact and approximate solution
     */
    private static double relativeError(final int optimalCost, final int approximateCost) {
        return (((double) (optimalCost - approximateCost)) / optimalCost);
    }

}
